<?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

/**
 * 自定义头像插件（修正版）
 *
 * @package CustomAvatar
 * @author 期望ai
 * @version 1.0.5
 * @link https://example.com
 */
class CustomAvatar_Plugin implements Typecho_Plugin_Interface
{
    private static $cachePrefix = 'custom_avatar_';
    // 用于在当前请求中跟踪已分配的头像文件名，以避免在同一页面加载中为不同用户随机分配相同的头像。
    // 结构: ['dir_name' => ['avatar_filename1' => true, 'avatar_filename2' => true, ...]]
    private static $assignedAvatarsPerRequest = [];

    public static function activate()
    {
        $uploadDir = self::getAvatarUploadDir();
        $paths = [
            $uploadDir,
            $uploadDir . '/bozhu',
            $uploadDir . '/bozhu/fixed_pool',
            $uploadDir . '/fangke'
        ];

        foreach ($paths as $path) {
            if (!is_dir($path)) {
                if (!mkdir($path, 0755, true)) {
                    throw new Typecho_Plugin_Exception('插件错误：无法创建文件夹 ' . $path . '，请检查插件目录权限。');
                }
            }
        }

        Typecho_Plugin::factory('Widget_Abstract_Comments')->gravatar = array('CustomAvatar_Plugin', 'renderAvatar');

        self::clearAllSessionCache();

        return _t('自定义头像插件已成功激活！');
    }

    public static function deactivate()
    {
        self::clearAllSessionCache();
        return _t('插件已禁用。头像文件保留在插件目录中。');
    }

    public static function config(Typecho_Widget_Helper_Form $form)
    {
        // 确保 session
        if (session_status() == PHP_SESSION_NONE) {
            session_start();
        }

        // 优先处理上传/删除请求（在输出 HTML 之前）
        // 这两个方法会在处理后做 303 重定向并 exit，避免之后继续输出
        self::handleDeleteRequest();
        self::handleBatchDeleteRequest();
        self::handleUploadRequest();

        // 输出配置页面
        echo <<<HTML
<style>
.avatar-manager { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
.avatar-manager h2 { margin-top: 0; }
.avatar-manager h3 { border-bottom: 1px solid #ddd; padding-bottom: 8px; margin-top: 20px;}
.avatar-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; margin-top: 10px; }
.avatar-item { 
    position: relative; 
    border: 1px solid #eee; 
    padding: 6px; 
    border-radius: 4px; 
    text-align: center; 
    background: #fff; 
    overflow: visible;
}
.avatar-item img { max-width: 100%; height: 100px; object-fit: contain; border-radius: 4px; }
.avatar-item .delete-btn { 
    position: absolute; 
    top: 4px; 
    right: 4px; 
    background: rgba(255, 0, 0, 0.8); 
    color: white; 
    border: none; 
    cursor: pointer; 
    border-radius: 3px; 
    padding: 2px 6px; 
    font-size: 12px; 
    z-index: 5; 
}
.avatar-item .delete-btn:hover { background: red; }
.avatar-item .select-checkbox {
    position: absolute;
    top: 4px;
    left: 4px;
    z-index: 5;
    width: 16px;
    height: 16px;
}
.upload-section { margin-top: 12px; padding: 12px; background: #f9f9f9; border: 1px dashed #ccc; border-radius: 4px; }
.batch-controls { 
    margin-top: 10px;
    margin-bottom: 12px; 
    padding: 10px; 
    background: #f5f5f5; 
    border-radius: 4px; 
    border: 1px solid #ddd;
}
.batch-controls button {
    margin-right: 10px;
    padding: 6px 12px;
    border: 1px solid #ddd;
    border-radius: 3px;
    cursor: pointer;
    background: #fff;
}
.batch-controls .batch-delete-btn {
    background: #dc3545;
    color: white;
    border-color: #dc3545;
}
.batch-controls .batch-delete-btn:hover {
    background: #c82333;
    border-color: #bd2130;
}
.batch-controls .batch-delete-btn:disabled {
    background: #6c757d;
    border-color: #6c757d;
    cursor: not-allowed;
}
.typecho-option {margin-bottom: 15px;}
.typecho-option label.typecho-label {font-weight: bold;}
.typecho-option input[type="file"] {margin-top: 6px;}
.typecho-option button {padding: 8px 15px; background: #467b96; color: white; border: none; border-radius: 3px; cursor: pointer;}
.typecho-option button:hover {background: #335d73;}
.avatar-selected { border: 3px solid #007bff; box-shadow: 0 0 6px rgba(0,123,255,0.2); }
.small-note { font-size: 12px; color: #666; margin-top: 6px; }

/* hover info */
.hover-info {
    position: absolute;
    top: -35px;
    background: #fff;
    color: #222;
    padding: 5px 10px;
    border-radius: 4px;
    font-size: 13px;
    white-space: nowrap;
    z-index: 100;
    display: none;
    pointer-events: none;
    border: 1px solid #ccc;
}
.avatar-item .hover-info { left: 50%; transform: translateX(-50%); }

.file-input-wrapper {
    position: relative;
    display: inline-block;
    vertical-align: middle;
}
.file-input-wrapper .selected-files-info {
    top: -55px;
}
</style>
<div class="avatar-manager">
    <h2>自定义头像管理</h2>
    <p>在此处上传、预览和管理博主与访客的头像。所有文件将存储在插件目录中：<code>CustomAvatar/assets/avatars</code></p>
    <p class="small-note">上传时文件名将保留原始名称（包含空格和部分特殊字符）并追加日期（YYYYMMDD），避免重名覆盖；在固定头像下拉中会显示去除日期后缀后的原始文件名以供选择。</p>
</div>
HTML;

        // 固定头像开关
        $enableFixed = new Typecho_Widget_Helper_Form_Element_Radio('enableFixed',
            [0 => _t('关闭 (使用博主随机头像)'), 1 => _t('开启 (使用博主固定头像)')],
            1, _t('博主固定头像开关'), _t('开启后，博主将显示固定的头像。关闭后，博主将从下方上传的随机头像中显示。'));
        $form->addInput($enableFixed);

        // 固定头像下拉（列出 bozhu/fixed_pool 中文件）
        $fixedAvatarOptions = ['' => _t('不选择固定头像')];
        $fixedPoolDir = self::getAvatarUploadDir() . '/bozhu/fixed_pool';
        $fixedFiles = is_dir($fixedPoolDir) ? array_diff(scandir($fixedPoolDir), array('.', '..')) : [];
        $fixedAvatars = array_values(preg_grep('/.*\.(jpg|jpeg|png|gif|webp)$/i', $fixedFiles));
        foreach ($fixedAvatars as $avatar) {
            $fixedAvatarOptions[$avatar] = self::getOriginalDisplayName($avatar);
        }

        $selectedFixedAvatar = new Typecho_Widget_Helper_Form_Element_Select(
            'selectedFixedAvatar',
            $fixedAvatarOptions,
            null,
            _t('选择博主固定头像'),
            _t('从已上传的图片中选择一张作为博主的固定头像。选择后请点击页面底部的"保存设置"以保存插件配置。')
        );
        $form->addInput($selectedFixedAvatar);

        // 管理区
        echo '<div class="avatar-manager"><h3>博主固定头像上传池（用于选择）</h3>';
        self::renderManagerSection('bozhu/fixed_pool', true, $selectedFixedAvatar->value);
        echo '</div>';

        echo '<div class="avatar-manager"><h3>博主随机头像（可多张上传）</h3>';
        self::renderManagerSection('bozhu', true);
        echo '</div>';

        echo '<div class="avatar-manager"><h3>访客随机头像（可多张上传）</h3>';
        self::renderManagerSection('fangke', true);
        echo '</div>';
    }

    public static function personalConfig(Typecho_Widget_Helper_Form $form) {}

    public static function renderAvatar($size, $rating, $default, $comments)
    {
        $options = Helper::options()->plugin('CustomAvatar');
        $isAuthor = ($comments->authorId == $comments->ownerId) && ($comments->authorId != 0);
        $avatarUrl = null;

        if ($isAuthor) {
            if ($options->enableFixed == 1) {
                $selectedFileName = $options->selectedFixedAvatar;
                if (!empty($selectedFileName)) {
                    $avatarUrl = self::getAvatarUploadUrl() . '/bozhu/fixed_pool/' . rawurlencode($selectedFileName);
                }
            } else {
                $cacheKey = 'author_random';
                $avatarUrl = self::getAvatarFromDir('bozhu', true, $cacheKey);
            }
        } else {
            $cacheKey = 'visitor_' . md5(strtolower($comments->mail));
            $avatarUrl = self::getAvatarFromDir('fangke', true, $cacheKey);
        }

        if ($avatarUrl) {
            $avatarUrlWithCacheBuster = $avatarUrl . (strpos($avatarUrl, '?') === false ? '?' : '&') . 't=' . time();
            echo '<img src="' . $avatarUrlWithCacheBuster . '" alt="' . htmlspecialchars($comments->author) . '" class="avatar" width="' . intval($size) . '" height="' . intval($size) . '">';
            return '';
        } else {
            return null;
        }
    }

    private static function getAvatarFromDir($dir, $isRandom, $cacheKey)
    {
        $fullCacheKey = self::$cachePrefix . $cacheKey;
        if (session_status() == PHP_SESSION_NONE) {
            session_start();
        }

        if (isset($_SESSION[$fullCacheKey])) {
            $cachedUrl = $_SESSION[$fullCacheKey];
            if (!isset(self::$assignedAvatarsPerRequest[$dir])) {
                self::$assignedAvatarsPerRequest[$dir] = [];
            }
            $cachedFileName = basename(parse_url($cachedUrl, PHP_URL_PATH));
            self::$assignedAvatarsPerRequest[$dir][$cachedFileName] = true;
            return $cachedUrl;
        }

        $avatarDir = self::getAvatarUploadDir() . '/' . $dir;
        if (!is_dir($avatarDir)) return null;

        $files = array_diff(scandir($avatarDir), array('.', '..'));
        $allAvatars = array_values(preg_grep('/.*\.(jpg|jpeg|png|gif|webp)$/i', $files));
        if (empty($allAvatars)) return null;

        if (!isset(self::$assignedAvatarsPerRequest[$dir])) {
            self::$assignedAvatarsPerRequest[$dir] = [];
        }

        $avatarFile = null;

        if ($isRandom) {
            $availableAvatarsForNewAssignment = [];
            foreach ($allAvatars as $fileName) {
                if (!isset(self::$assignedAvatarsPerRequest[$dir][$fileName])) {
                    $availableAvatarsForNewAssignment[] = $fileName;
                }
            }

            if (!empty($availableAvatarsForNewAssignment)) {
                $avatarFile = $availableAvatarsForNewAssignment[array_rand($availableAvatarsForNewAssignment)];
            } else {
                $avatarFile = $allAvatars[array_rand($allAvatars)];
            }
        } else {
            $avatarFile = $allAvatars[0];
        }

        if (!$avatarFile) return null;

        $url = self::getAvatarUploadUrl() . '/' . $dir . '/' . rawurlencode($avatarFile);
        $_SESSION[$fullCacheKey] = $url;
        self::$assignedAvatarsPerRequest[$dir][$avatarFile] = true;

        return $url;
    }

    private static function renderManagerSection($path, $isMultiple, $selectedFixedAvatarFileName = null)
    {
        $uploadDir = self::getAvatarUploadDir() . '/' . $path;
        $uploadUrl = self::getAvatarUploadUrl() . '/' . $path;
        $files = is_dir($uploadDir) ? array_diff(scandir($uploadDir), array('.', '..')) : [];
        $avatars = array_values(preg_grep('/.*\.(jpg|jpeg|png|gif|webp)$/i', $files));

        // 头像网格
        echo '<div class="avatar-grid" data-section="' . str_replace('/', '-', $path) . '">';
        if (!empty($avatars)) {
            foreach ($avatars as $avatar) {
                $fileUrl = $uploadUrl . '/' . rawurlencode($avatar) . '?v=' . time();
                $filePath = $path . '/' . $avatar;
                $isSelectedClass = ($path === 'bozhu/fixed_pool' && $avatar === $selectedFixedAvatarFileName) ? ' avatar-selected' : '';
                $originalDisplayName = self::getOriginalDisplayName($avatar);

                echo '<div class="avatar-item' . $isSelectedClass . '">';
                echo '<input type="checkbox" class="select-checkbox" data-file="' . htmlspecialchars($filePath) . '" onchange="updateSelectedCount(\'' . str_replace('/', '-', $path) . '\')">';
                echo '<div class="hover-info"></div>';
                echo '<img src="' . $fileUrl . '" alt="' . htmlspecialchars($avatar) . '" ';
                echo 'data-filename="' . htmlspecialchars($originalDisplayName, ENT_QUOTES) . '" ';
                echo 'title="">';
                echo '<form method="post" onsubmit="return confirm(\'确定要删除这个头像吗？\\n该操作不可撤销。\');" style="display:inline-block;margin-top:6px;">';
                echo '<input type="hidden" name="action" value="delete_avatar">';
                echo '<input type="hidden" name="file" value="' . htmlspecialchars($filePath) . '">';
                echo '<button type="submit" class="delete-btn" data-tooltip-text="删除头像" title="">×</button>';
                echo '</form>';
                if ($path === 'bozhu/fixed_pool') {
                    echo '<div style="margin-top:8px;">';
                    echo '<button type="button" onclick="selectFixedAvatar(\'' . htmlspecialchars($avatar, ENT_QUOTES) . '\');" style="padding:6px 8px;border-radius:3px;border:1px solid #ddd;background:#fff;cursor:pointer;">选择为固定头像</button>';
                    echo '</div>';
                }
                echo '</div>';
            }
        } else {
            echo '<p>暂无头像，请上传。</p>';
        }
        echo '</div>';

        // 始终显示的批量操作控制区（即使没有头像）
        $sectionId = str_replace('/', '-', $path);
        echo '<div class="batch-controls" data-section="' . $sectionId . '">';
        echo '<button type="button" onclick="toggleSelectAll(\'' . $sectionId . '\')">全选/取消全选</button>';
        echo '<button type="button" class="batch-delete-btn" onclick="batchDelete(\'' . $path . '\', \'' . $sectionId . '\')">批量删除选中</button>';
        echo '<span id="selected-count-' . $sectionId . '">已选择: 0 个</span>';
        echo '</div>';

        // 上传区域（位于批量操作下方）
        echo '<div class="upload-section">';
        $formId = 'upload-form-' . str_replace('/', '-', $path);
        $fileInputId = 'file-' . str_replace('/', '-', $path);
        echo '<form id="' . $formId . '" method="post" enctype="multipart/form-data">';
        echo '<input type="hidden" name="action" value="upload_avatar">';
        echo '<input type="hidden" name="path" value="' . htmlspecialchars($path) . '">';
        echo '<label for="' . $fileInputId . '">上传新头像:</label><br>';
        echo '<div class="file-input-wrapper">';
        echo '<input type="file" name="avatars[]" id="' . $fileInputId . '" ' . ($isMultiple ? 'multiple' : '') . ' accept="image/*" title="">';
        echo '<div class="selected-files-info hover-info"></div>';
        echo '</div>';
        echo '&nbsp;<button type="submit" onclick="return validateUpload(\'' . $fileInputId . '\');">上传</button>';
        echo '</form>';
        echo '<div class="small-note">支持 JPG/PNG/GIF/WEBP。文件名将保留原名（包含空格和部分特殊字符）并追加日期（YYYYMMDD），以避免冲突。</div>';
        echo '</div>';

        // JS：控制批量操作与上传区域的交互
        echo <<<JS
<script>
function validateUpload(fileInputId) {
    var fileInput = document.getElementById(fileInputId);
    if (!fileInput) return true;
    if (!fileInput.files || fileInput.files.length === 0) {
        alert('请选择至少一张图片进行上传。');
        return false;
    }
    return true;
}

function selectFixedAvatar(filename) {
    var sel = document.querySelector('select[name="selectedFixedAvatar"]');
    if (!sel) {
        alert('未找到固定头像选择框，请在页面上方选择并保存。');
        return;
    }
    var opt = Array.prototype.slice.call(sel.options).find(function(o){ return o.value === filename; });
    if (opt) {
        sel.value = filename;
        alert('已在下拉框中选择：' + opt.textContent + '\\n请点击页面底部的"保存设置"以保存固定头像选择。');
    } else {
        alert('下拉中未找到该文件选项。请先刷新页面或确保已上传并保存，然后再次选择。');
    }
}

function toggleSelectAll(sectionId) {
    var grid = document.querySelector('.avatar-grid[data-section="' + sectionId + '"]');
    if (!grid) {
        // 没有头像网格（或为空），不做任何操作
        updateSelectedCount(sectionId);
        return;
    }
    
    var checkboxes = grid.querySelectorAll('.select-checkbox');
    if (!checkboxes || checkboxes.length === 0) {
        updateSelectedCount(sectionId);
        return;
    }

    var allChecked = Array.prototype.every.call(checkboxes, function(cb) { return cb.checked; });
    
    Array.prototype.forEach.call(checkboxes, function(cb) {
        cb.checked = !allChecked;
    });
    
    updateSelectedCount(sectionId);
}

function updateSelectedCount(sectionId) {
    var grid = document.querySelector('.avatar-grid[data-section="' + sectionId + '"]');
    var countSpan = document.getElementById('selected-count-' + sectionId);
    var batchControl = document.querySelector('.batch-controls[data-section="' + sectionId + '"]');
    if (!countSpan) return;
    
    var checkedCount = 0;
    if (grid) {
        var checkboxes = grid.querySelectorAll('.select-checkbox');
        checkedCount = Array.prototype.filter.call(checkboxes, function(cb) { return cb.checked; }).length;
    }
    countSpan.textContent = '已选择: ' + checkedCount + ' 个';

    // 设置批量删除按钮状态
    if (batchControl) {
        var deleteBtn = batchControl.querySelector('.batch-delete-btn');
        if (deleteBtn) {
            deleteBtn.disabled = checkedCount === 0;
        }
    }
}

function batchDelete(path, sectionId) {
    var grid = document.querySelector('.avatar-grid[data-section="' + sectionId + '"]');
    if (!grid) {
        alert('当前目录没有头像可删除。');
        return;
    }
    
    var checkboxes = grid.querySelectorAll('.select-checkbox:checked');
    if (!checkboxes || checkboxes.length === 0) {
        alert('请选择要删除的头像。');
        return;
    }
    
    var files = Array.prototype.map.call(checkboxes, function(cb) {
        return cb.getAttribute('data-file');
    });
    
    if (!confirm('确定要删除选中的 ' + files.length + ' 个头像吗？\\n该操作不可撤销。')) {
        return;
    }
    
    var form = document.createElement('form');
    form.method = 'POST';
    form.style.display = 'none';
    
    var actionInput = document.createElement('input');
    actionInput.type = 'hidden';
    actionInput.name = 'action';
    actionInput.value = 'batch_delete_avatars';
    form.appendChild(actionInput);
    
    files.forEach(function(file) {
        var fileInput = document.createElement('input');
        fileInput.type = 'hidden';
        fileInput.name = 'files[]';
        fileInput.value = file;
        form.appendChild(fileInput);
    });
    
    document.body.appendChild(form);
    form.submit();
}

document.addEventListener('DOMContentLoaded', function() {
    // 初始化所有section的选择计数（无头像也会初始化并禁用删除按钮）
    document.querySelectorAll('.batch-controls[data-section]').forEach(function(bc) {
        var sectionId = bc.getAttribute('data-section');
        updateSelectedCount(sectionId);
    });

    // avatar hover & delete tooltip
    document.querySelectorAll('.avatar-item').forEach(function(item) {
        var hoverInfo = item.querySelector('.hover-info');
        var img = item.querySelector('img');
        var deleteBtn = item.querySelector('.delete-btn');

        if (img) {
            img.addEventListener('mouseenter', function() {
                if (this.dataset.filename) {
                    hoverInfo.textContent = this.dataset.filename;
                    hoverInfo.style.display = 'block';
                }
            });
            img.addEventListener('mouseleave', function() {
                hoverInfo.style.display = 'none';
            });
        }

        if (deleteBtn) {
            deleteBtn.addEventListener('mouseenter', function() {
                if (this.dataset.tooltipText) {
                    hoverInfo.textContent = this.dataset.tooltipText;
                    hoverInfo.style.display = 'block';
                }
            });
            deleteBtn.addEventListener('mouseleave', function() {
                hoverInfo.style.display = 'none';
            });
        }
    });

    // file input selected info
    document.querySelectorAll('.file-input-wrapper').forEach(function(wrapper) {
        var fileInput = wrapper.querySelector('input[type="file"]');
        var selectedFilesInfo = wrapper.querySelector('.selected-files-info');

        if (fileInput && selectedFilesInfo) {
            function updateSelectedFileNames() {
                var names = [];
                if (fileInput.files && fileInput.files.length > 0) {
                    for (var i = 0; i < fileInput.files.length; i++) {
                        names.push(fileInput.files[i].name);
                    }
                }
                if (names.length > 0) {
                    selectedFilesInfo.textContent = names.join(', ');
                    selectedFilesInfo.style.left = '50%';
                    selectedFilesInfo.style.transform = 'translateX(-50%)';
                } else {
                    selectedFilesInfo.textContent = '未选择文件';
                    selectedFilesInfo.style.left = '0';
                    selectedFilesInfo.style.transform = 'none';
                }
            }
            updateSelectedFileNames();
            fileInput.addEventListener('change', updateSelectedFileNames);

            function toggleSelectedFilesInfo(show) {
                selectedFilesInfo.style.display = show ? 'block' : 'none';
            }
            fileInput.addEventListener('mouseenter', function() { toggleSelectedFilesInfo(true); });
            fileInput.addEventListener('mouseleave', function() { toggleSelectedFilesInfo(false); });
            wrapper.addEventListener('mouseenter', function() { toggleSelectedFilesInfo(true); });
            wrapper.addEventListener('mouseleave', function() { toggleSelectedFilesInfo(false); });
        }
    });
});
</script>
JS;
    }

    private static function handleUploadRequest()
    {
        if (!isset($_POST['action']) || $_POST['action'] !== 'upload_avatar') {
            return;
        }

        if (!isset($_POST['path'])) {
            self::redirectToConfig();
        }
        $path = $_POST['path'];
        $uploadDir = self::getAvatarUploadDir() . '/' . $path;

        if (!is_dir($uploadDir)) {
            @mkdir($uploadDir, 0755, true);
        }

        if (!isset($_FILES['avatars'])) {
            self::redirectToConfig();
        }

        $files = $_FILES['avatars'];
        $uploadedCount = 0;

        $names = is_array($files['name']) ? $files['name'] : [$files['name']];
        $tmps = is_array($files['tmp_name']) ? $files['tmp_name'] : [$files['tmp_name']];
        $errors = is_array($files['error']) ? $files['error'] : [$files['error']];

        foreach ($names as $key => $name) {
            if (!isset($errors[$key]) || $errors[$key] === UPLOAD_ERR_NO_FILE) {
                continue;
            }
            if ($errors[$key] !== UPLOAD_ERR_OK) {
                error_log("CustomAvatar: 文件上传错误 code=" . intval($errors[$key]) . " for file " . $name);
                continue;
            }

            $tmpName = $tmps[$key];
            $fileName = basename($name);

            if (empty($tmpName) || !is_uploaded_file($tmpName)) {
                error_log("CustomAvatar: 无效的临时文件: " . var_export($tmpName, true));
                continue;
            }

            $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            $mimeType = finfo_file($finfo, $tmpName);
            finfo_close($finfo);

            if (!in_array($mimeType, $allowedTypes)) {
                error_log("CustomAvatar: 非法文件类型尝试上传: " . $mimeType . " for file " . $fileName);
                continue;
            }

            $ext = pathinfo($fileName, PATHINFO_EXTENSION);
            $base = pathinfo($fileName, PATHINFO_FILENAME);
            $base = str_replace(['/', '\\', ':', '*', '?', '"', '<', '>', '|'], '', $base);
            $base = trim($base);
            if (empty($base)) {
                $base = 'untitled';
            }

            $date = date('Ymd');
            $targetName = $base . '_' . $date . '.' . strtolower($ext);
            $targetPath = $uploadDir . '/' . $targetName;

            if (file_exists($targetPath)) {
                $targetName = $base . '_' . $date . '_' . substr(md5(uniqid('', true)), 0, 6) . '.' . strtolower($ext);
                $targetPath = $uploadDir . '/' . $targetName;
            }

            if (move_uploaded_file($tmpName, $targetPath)) {
                @chmod($targetPath, 0644);
                $uploadedCount++;
            } else {
                error_log("CustomAvatar: 文件移动失败: " . $tmpName . " -> " . $targetPath);
            }
        }

        if ($uploadedCount > 0) {
            self::clearAllSessionCache();
        }

        self::redirectToConfig();
    }

    private static function handleDeleteRequest()
    {
        if (!isset($_POST['action']) || $_POST['action'] !== 'delete_avatar') {
            return;
        }
        if (empty($_POST['file'])) {
            self::redirectToConfig();
        }

        $fileToDelete = $_POST['file'];
        $filePath = self::getAvatarUploadDir() . '/' . $fileToDelete;

        $realUploadDir = realpath(self::getAvatarUploadDir());
        $realFilePath = realpath($filePath);

        if ($realFilePath && $realUploadDir && strpos($realFilePath, $realUploadDir) === 0 && file_exists($realFilePath)) {
            if (@unlink($realFilePath)) {
                self::clearAllSessionCache();
            } else {
                error_log("CustomAvatar: 无法删除文件: " . $realFilePath);
            }
        } else {
            error_log("CustomAvatar: 尝试删除不在允许范围内的文件或文件不存在: " . $fileToDelete);
        }

        self::redirectToConfig();
    }

    private static function handleBatchDeleteRequest()
    {
        if (!isset($_POST['action']) || $_POST['action'] !== 'batch_delete_avatars') {
            return;
        }

        if (empty($_POST['files']) || !is_array($_POST['files'])) {
            self::redirectToConfig();
        }

        $deletedCount = 0;
        $realUploadDir = realpath(self::getAvatarUploadDir());

        foreach ($_POST['files'] as $fileToDelete) {
            if (empty($fileToDelete)) continue;

            $filePath = self::getAvatarUploadDir() . '/' . $fileToDelete;
            $realFilePath = realpath($filePath);

            if ($realFilePath && $realUploadDir && strpos($realFilePath, $realUploadDir) === 0 && file_exists($realFilePath)) {
                if (@unlink($realFilePath)) {
                    $deletedCount++;
                } else {
                    error_log("CustomAvatar: 批量删除时无法删除文件: " . $realFilePath);
                }
            } else {
                error_log("CustomAvatar: 批量删除时尝试删除不在允许范围内的文件或文件不存在: " . $fileToDelete);
            }
        }

        if ($deletedCount > 0) {
            self::clearAllSessionCache();
        }

        self::redirectToConfig();
    }

    private static function redirectToConfig()
    {
        $target = Typecho_Common::url('options-plugin.php?config=CustomAvatar', Helper::options()->adminUrl);

        if (!headers_sent()) {
            header('HTTP/1.1 303 See Other');
            header('Location: ' . $target);
            echo '<!doctype html><html><head><meta charset="utf-8"><title>Redirecting...</title>';
            echo '<meta http-equiv="refresh" content="0;url=' . htmlspecialchars($target) . '">';
            echo '<script>try{window.location.replace("' . addslashes($target) . '");}catch(e){window.location.href="' . addslashes($target) . '";}</script>';
            echo '</head><body>If your browser does not automatically redirect, <a href="' . htmlspecialchars($target) . '">click here</a>.</body></html>';
        } else {
            echo '<script>window.location.href="' . addslashes($target) . '";</script>';
        }
        exit;
    }

    private static function clearAllSessionCache()
    {
        if (session_status() == PHP_SESSION_NONE) {
            session_start();
        }
        foreach ($_SESSION as $key => $value) {
            if (strpos($key, self::$cachePrefix) === 0) {
                unset($_SESSION[$key]);
            }
        }
    }

    private static function getOriginalDisplayName($storedFileName) {
        $ext = pathinfo($storedFileName, PATHINFO_EXTENSION);
        $base = pathinfo($storedFileName, PATHINFO_FILENAME);

        if (preg_match('/^(.*?)_(\d{8}(_[a-f0-9]{6})?)$/i', $base, $matches)) {
            $originalBase = $matches[1];
        } else {
            $originalBase = $base;
        }
        return $originalBase . (empty($ext) ? '' : '.' . $ext);
    }

    private static function getAvatarUploadDir()
    {
        return __DIR__ . '/assets/avatars';
    }

    private static function getAvatarUploadUrl()
    {
        return Typecho_Common::url('CustomAvatar/assets/avatars', Helper::options()->pluginUrl);
    }
}